package edu.towson.cis.cosc442.project3.vendingmachine;


import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * The unit test Class for VendingMachine.
 */
public class VendingMachineTest {
	
	/** Declaring necessary test objects for {@link VendingMachine} */
	VendingMachineItem vendingMachineItem;
	VendingMachine vendingMachine;
	
	/**
	 * Initializes the necessary test objects for the test cases to use.
	 *
	 * @throws Exception the exception
	 */
	@Before
	public void setUp() throws Exception
	{
		vendingMachine = new VendingMachine();
		vendingMachineItem = new VendingMachineItem("TestItemOne", 4.99);
	}	
	
	/**
	 * Test for the addItem() method of the {@link VendingMachine} class with appropriate code "A".
	 */
	@Test
	public void testAddItem()
	{
		vendingMachine.addItem(vendingMachineItem, VendingMachine.A_CODE);
		assertSame(vendingMachineItem, vendingMachine.getItem(VendingMachine.A_CODE));
	}
	
	/**
	 * Tests that the VendingMachineException class is called when trying to add an item with an incorrect "code".
	 */
	@Test(expected = VendingMachineException.class)
	public void testAddItemIncorrectCodeException()
	{
		//test the default constructor
		VendingMachineException e = new VendingMachineException();
		vendingMachine.addItem(vendingMachineItem, "E");
	}
	
	/**
	 * Tests that the VendingMachineException class is called when trying to add an item in a slot that is already taken
	 */
	@Test(expected = VendingMachineException.class)
	public void testAddItemInSameSlotException()
	{
		vendingMachine.addItem(vendingMachineItem, VendingMachine.B_CODE);
		assertSame(vendingMachineItem, vendingMachine.getItem(VendingMachine.B_CODE));
		
		vendingMachine.addItem(vendingMachineItem, VendingMachine.B_CODE);
		assertSame(vendingMachineItem, vendingMachine.getItem(VendingMachine.B_CODE));
	}
	
	/**
	 * Test the removeItem() method of the {@link VendingMachine} class with appropriate code "A" and item in A.
	 */
	@Test
	public void testRemoveItem()
	{
		vendingMachine.addItem(vendingMachineItem, VendingMachine.C_CODE);
		vendingMachine.removeItem(VendingMachine.C_CODE);
		assertEquals(null, vendingMachine.getItem(VendingMachine.C_CODE)); //verify that the item is removed
	}
	
	/**
	 * Test the removeItem() method of the {@link VendingMachine} class with inappropriate code "E".
	 */
	@Test(expected = VendingMachineException.class)
	public void testRemoveItemFromInvalidCode()
	{
		vendingMachine.removeItem("E");
	}
	
	/**
	 * Test the removeItem() method of the {@link VendingMachine} class with slot code that is empty.
	 */
	@Test(expected = VendingMachineException.class)
	public void testRemoveItemFromEmptySlot()
	{
		vendingMachine.removeItem(VendingMachine.D_CODE);
	}
	
	/**
	 *  Test the insertMoney() method of the {@link VendingMachine} class with insert amount > 0
	 */
	@Test
	public void testInsertMoneyGreater0()
	{
		vendingMachine.insertMoney(1.99);
		assertEquals(1.99, vendingMachine.balance, 0.0001);
	}
	
	/**
	 *  Test the insertMoney() method of the {@link VendingMachine} class with insert amount < 0
	 */
	@Test(expected = VendingMachineException.class)
	public void testInsertMoneyLess0()
	{
		vendingMachine.insertMoney(-1.99);
	}
	
	/**
	 *  Test the insertMoney() method of the {@link VendingMachine} class with insert amount == 0
	 */
	@Test
	public void testInsertMoneyEqual0()
	{
		vendingMachine.insertMoney(0);
		assertEquals(0, vendingMachine.balance, 0.0001);
	}
	
	/**
	 * Test the getBalance() method of the {@link VendingMachine} class with balance of 0.
	 */
	@Test
	public void testGetBalance0()
	{
		assertEquals(0, vendingMachine.getBalance(), 0.001);
	}
	
	/**
	 * Test the getBalance() method of the {@link VendingMachine} class with positive balance.
	 */
	@Test
	public void testGetBalancePositive()
	{
		vendingMachine.balance = 1.99;
		assertEquals(1.99, vendingMachine.getBalance(), 0.001);
	}
	
	/**
	 * Test the getBalance() method of the {@link VendingMachine} class with negative balance.
	 */
	@Test
	public void testGetBalanceNegative()
	{
		vendingMachine.balance = -1.99;
		assertEquals(0.00, vendingMachine.getBalance(), 0.001);
	}
	
	/**
	 * Test the makePurchase() method of the {@link VendineMachine} class with item price less than the current balance
	 */
	@Test
	public void testMakePurchaseLessThanBalance()
	{
		vendingMachine.addItem(vendingMachineItem, VendingMachine.A_CODE);
		vendingMachine.balance = 5.99;
		assertTrue(vendingMachine.makePurchase(VendingMachine.A_CODE));
		assertEquals(1.00,vendingMachine.balance, 0.001); //ensure balance changed correctly
	}
	
	/**
	 * Test the makePurchase() method of the {@link VendineMachine} class with item price equal to the current balance. 4.99 == 4.99
	 */
	@Test
	public void testMakePurchaseEqualToBalance()
	{
		vendingMachine.addItem(vendingMachineItem, VendingMachine.A_CODE);
		vendingMachine.balance = 4.99;
		assertTrue(vendingMachine.makePurchase(VendingMachine.A_CODE));
		assertEquals(0,vendingMachine.balance, 0.001); //ensure balance changed correctly
	}
	
	/**
	 * Test the makePurchase() method of the {@link VendineMachine} class with item price greater than the current balance. 4.99 > 3.99
	 */
	@Test
	public void testMakePurchaseGreaterThanBalance()
	{
		vendingMachine.addItem(vendingMachineItem, VendingMachine.A_CODE);
		vendingMachine.balance = 3.99;
		assertFalse(vendingMachine.makePurchase(VendingMachine.A_CODE));
		assertEquals(3.99,vendingMachine.balance, 0.001); //ensure balance did not change
	}
	
	/**
	 * Test the makePurchase() method of the {@link VendineMachine} class with selected slot that is empty
	 */
	@Test
	public void testMakePurchaseEmptySlot()
	{
	    assertFalse(vendingMachine.makePurchase(VendingMachine.A_CODE));
	}
	
	/**
	 * Test the makePurchase() method of the {@link VendingMachine} class with negative balance
	 */
	@Test
	public void testMakePurchaseNegativeBalance()
	{
		vendingMachine.balance = -1.99;
		assertFalse(vendingMachine.makePurchase(VendingMachine.A_CODE));
		assertEquals(0, vendingMachine.balance, 0.001); //should set balance back to 0 once it realizes it was negative
	}

	/**
	 * Test the returnChange() method of the {@link VendineMachine} class with positive balance
	 */
	@Test
	public void testReturnChangePositiveBalance()
	{
		vendingMachine.balance = 1.00;
		assertEquals(1.00, vendingMachine.returnChange(), 0.001);
	}
	
	/**
	 * Test the returnChange() method of the {@link VendineMachine} class with balance of 0
	 */
	@Test
	public void testReturnChangeBalance0()
	{
		assertEquals(0, vendingMachine.returnChange(), 0.001);
	}
	
	/**
	 * Test the returnChange() method of the {@link VendineMachine} class with negative balance
	 */
	@Test
	public void testReturnChangeNegativeBalance()
	{
		vendingMachine.balance = -1.00;
		assertEquals(0.00, vendingMachine.returnChange(), 0.001);
	}
	
	/**
	 * Cleans up test objects after a test case is executed.
	 */
	@After
	public void tearDown(){
		vendingMachine = null;
		vendingMachineItem = null;
	}
}
